// ZipRegistry.cpp

#include "StdAfx.h"

#include "Common/String/StringConvert.h"

#include "Common/FileSystem/FileDir.h"
#include "Common/FileSystem/FileName.h"
#include "Common/Registry/Registry.h"

#include "AppRegistry.h"

#include "Common/FileSystem/Wildcard.h"
#include "Common/System/Threading/Utility.h"

static CComAutoCriticalSection g_CS;
#define CS_LOCK Common::System::Threading::CComScopeAutoLock lock(g_CS);

static const TCHAR *kCuPrefix = _T("Software") STRING_PATH_SEPARATOR _T("7-Zip") STRING_PATH_SEPARATOR;


namespace Common
{
using namespace System;


static CString GetKeyPath(const CString &path) { return kCuPrefix + path; }

static LONG OpenMainKey(Common::Registry::CKey &key, LPCTSTR keyName)
{
  return key.Open(HKEY_CURRENT_USER, GetKeyPath(keyName), KEY_READ);
}

static LONG CreateMainKey(Common::Registry::CKey &key, LPCTSTR keyName)
{
  return key.Create(HKEY_CURRENT_USER, GetKeyPath(keyName));
}

namespace Archive
{

namespace Extract
{

static const TCHAR *kKeyName = TEXT("Extraction");

static const TCHAR *kExtractMode = TEXT("ExtractMode");
static const TCHAR *kOverwriteMode = TEXT("OverwriteMode");
static const TCHAR *kShowPassword = TEXT("ShowPassword");
static const TCHAR *kPathHistory = TEXT("PathHistory");

void CInfo::Save() const
{
  CS_LOCK
  Common::Registry::CKey key;
  CreateMainKey(key, kKeyName);
  key.SetValue(kExtractMode, (UINT32)PathMode);
  key.SetValue(kOverwriteMode, (UINT32)OverwriteMode);
  key.SetValue(kShowPassword, ShowPassword);
  key.RecurseDeleteKey(kPathHistory);
  key.SetValue_Strings(kPathHistory, Paths);
}


void CInfo::Load()
{
	PathMode = Archive::Extract::PathMode::kCurrentPathnames;
  OverwriteMode = Archive::Extract::OverwriteMode::kAskBefore;
  ShowPassword = false;
  Paths.clear();

  CS_LOCK
  Common::Registry::CKey key;
  if (OpenMainKey(key, kKeyName) != ERROR_SUCCESS)
	return;
  
  key.GetValue_Strings(kPathHistory, Paths);
  UINT32 v;
  if (key.QueryValue(kExtractMode, v) == ERROR_SUCCESS && v <= static_cast<UINT32>(Common::Archive::Extract::PathMode::kNoPathnames))
	PathMode = (Common::Archive::Extract::PathMode)v;
  if (key.QueryValue(kOverwriteMode, v) == ERROR_SUCCESS && v <= static_cast<UINT32>(Common::Archive::Extract::OverwriteMode::kAutoRenameExisting))
	  OverwriteMode = (Common::Archive::Extract::OverwriteMode)v;
  key.GetValue_IfOk(kShowPassword, ShowPassword);
}

}

namespace Compression
{

static const TCHAR *kKeyName = TEXT("Compression");

static const TCHAR *kArcHistory = TEXT("ArcHistory");
static const TCHAR *kArchiver = _T("Archiver");
static const TCHAR *kShowPassword = TEXT("ShowPassword");
static const TCHAR *kEncryptHeaders = TEXT("EncryptHeaders");

static const TCHAR *kOptionsKeyName = TEXT("Options");

static const TCHAR *kLevel = TEXT("Level");
static const TCHAR *kDictionary = TEXT("Dictionary");
static const TCHAR *kOrder = TEXT("Order");
static const TCHAR *kBlockSize = TEXT("BlockSize");
static const TCHAR *kNumThreads = TEXT("NumThreads");
static const TCHAR *kMethod = _T("Method");
static const TCHAR *kOptions = _T("Options");
static const TCHAR *kEncryptionMethod = _T("EncryptionMethod");

static void SetRegString(Common::Registry::CKey &key, const WCHAR *name, const CString &value)
{
  if (value.IsEmpty())
	key.DeleteValue(name);
  else
	key.SetValue(name, value);
}

static void SetRegUInt32(Common::Registry::CKey &key, const TCHAR *name, UINT32 value)
{
  if (value == (UINT32)-1)
	key.DeleteValue(name);
  else
	key.SetValue(name, value);
}

static void GetRegString(Common::Registry::CKey &key, const WCHAR *name, CString &value)
{
  if (key.QueryValue(name, value) != ERROR_SUCCESS)
	value.Empty();
}

static void GetRegUInt32(Common::Registry::CKey &key, const TCHAR *name, UINT32 &value)
{
  if (key.QueryValue(name, value) != ERROR_SUCCESS)
	value = (UINT32)-1;
}

void CInfo::Save() const
{
  CS_LOCK

  Common::Registry::CKey key;
  CreateMainKey(key, kKeyName);
  key.SetValue(kLevel, (UINT32)Level);
  key.SetValue(kArchiver, ArcType);
  key.SetValue(kShowPassword, ShowPassword);
  key.SetValue(kEncryptHeaders, EncryptHeaders);
  key.RecurseDeleteKey(kArcHistory);
  key.SetValue_Strings(kArcHistory, ArcPaths);

  key.RecurseDeleteKey(kOptionsKeyName);
  {
	Common::Registry::CKey optionsKey;
	optionsKey.Create(key, kOptionsKeyName);
	for (const CFormatOptions &fo : Formats)
	{
	  Common::Registry::CKey fk;
	  fk.Create(optionsKey, fo.FormatID);
	  
	  SetRegUInt32(fk, kLevel, fo.Level);
	  SetRegUInt32(fk, kDictionary, fo.Dictionary);
	  SetRegUInt32(fk, kOrder, fo.Order);
	  SetRegUInt32(fk, kBlockSize, fo.BlockLogSize);
	  SetRegUInt32(fk, kNumThreads, fo.NumThreads);

	  SetRegString(fk, kMethod, fo.Method);
	  SetRegString(fk, kOptions, fo.Options);
	  SetRegString(fk, kEncryptionMethod, fo.EncryptionMethod);
	}
  }
}

void CInfo::Load()
{
  ArcPaths.clear();
  Formats.clear();

  Level = 5;
  ArcType = _T("7z");
  ShowPassword = false;
  EncryptHeaders = false;

  CS_LOCK
  Common::Registry::CKey key;

  if (OpenMainKey(key, kKeyName) != ERROR_SUCCESS)
	return;

  key.GetValue_Strings(kArcHistory, ArcPaths);
  
  {
	Common::Registry::CKey optionsKey;
	if (optionsKey.Open(key, kOptionsKeyName, KEY_READ) == ERROR_SUCCESS)
	{
	  CStringVector formatIDs;
	  optionsKey.EnumKeys(formatIDs);
	  Formats.reserve(formatIDs.size());
	  for (size_t i = 0; i < formatIDs.size(); i++)
	  {
		Common::Registry::CKey fk;
		CFormatOptions fo;
		fo.FormatID = formatIDs[i];
		if (fk.Open(optionsKey, fo.FormatID, KEY_READ) == ERROR_SUCCESS)
		{
		  GetRegString(fk, kOptions, fo.Options);
		  GetRegString(fk, kMethod, fo.Method);
		  GetRegString(fk, kEncryptionMethod, fo.EncryptionMethod);

		  GetRegUInt32(fk, kLevel, fo.Level);
		  GetRegUInt32(fk, kDictionary, fo.Dictionary);
		  GetRegUInt32(fk, kOrder, fo.Order);
		  GetRegUInt32(fk, kBlockSize, fo.BlockLogSize);
		  GetRegUInt32(fk, kNumThreads, fo.NumThreads);

		  Formats.push_back(fo);
		}
	  }
	}
  }

  CString a;
  if (key.QueryValue(kArchiver, a) == ERROR_SUCCESS)
	ArcType = a;
  key.GetValue_IfOk(kLevel, Level);
  key.GetValue_IfOk(kShowPassword, ShowPassword);
  key.GetValue_IfOk(kEncryptHeaders, EncryptHeaders);
}

}

static const TCHAR *kOptionsInfoKeyName = TEXT("Options");

static const TCHAR *kCascadedMenu = TEXT("CascadedMenu");
static const TCHAR *kContextMenu = TEXT("ContextMenu");

void CContextMenuInfo::Save() const
{
  CS_LOCK
  Common::Registry::CKey key;
  CreateMainKey(key, kOptionsInfoKeyName);
  key.SetValue(kCascadedMenu, Cascaded);
  key.SetValue(kContextMenu, Flags);
}

void CContextMenuInfo::Load()
{
  Cascaded = TRUE;
  Flags = (UINT32)-1;
  CS_LOCK
  Common::Registry::CKey key;
  if (OpenMainKey(key, kOptionsInfoKeyName) != ERROR_SUCCESS)
	return;
  UINT32 value;
  key.GetValue_IfOk(kCascadedMenu, value);
  Cascaded = value;
  key.GetValue_IfOk(kContextMenu, value);
  Flags = value;
}

namespace WorkDir
{
static const TCHAR *kWorkDirType = TEXT("WorkDirType");
static const TCHAR *kWorkDirPath = _T("WorkDirPath");
static const TCHAR *kTempRemovableOnly = TEXT("TempRemovableOnly");


void CInfo::Save()const
{
  CS_LOCK
  Registry::CKey key;
  Common::CreateMainKey(key, kOptionsInfoKeyName);
  key.SetValue(kWorkDirType, (UINT32)Mode);
  key.SetValue(kWorkDirPath, Path);
  key.SetValue(kTempRemovableOnly, ForRemovableOnly);
}

void CInfo::Load()
{
  SetDefault();

  CS_LOCK
  Common::Registry::CKey key;
  if (Common::OpenMainKey(key, kOptionsInfoKeyName) != ERROR_SUCCESS)
	return;

  UINT32 dirType;
  if (key.QueryValue(kWorkDirType, dirType) != ERROR_SUCCESS)
	return;
  switch (dirType)
  {
	case WorkDir::Mode::kSystem:
	case WorkDir::Mode::kCurrent:
	case WorkDir::Mode::kSpecified:
		Mode = static_cast<WorkDir::Mode>(dirType);
  }
  if (key.QueryValue(kWorkDirPath, Path) != ERROR_SUCCESS)
  {
	Path.Empty();
	if (Mode == WorkDir::Mode::kSpecified)
	  Mode = WorkDir::Mode::kSystem;
  }
  key.GetValue_IfOk(kTempRemovableOnly, ForRemovableOnly);
}


CString GetWorkDir(const WorkDir::CInfo &workDirInfo, const CString &path)
{
  WorkDir::Mode mode = workDirInfo.Mode;
  if (workDirInfo.ForRemovableOnly)
  {
	mode = WorkDir::Mode::kCurrent;
	CString prefix = path.Left(3);
	if (prefix[1] == L':' && prefix[2] == L'\\')
	{
	  UINT driveType = ::GetDriveType(String::GetSystemString(prefix, ::AreFileApisANSI() ? CP_ACP : CP_OEMCP));
	  if (driveType == DRIVE_CDROM || driveType == DRIVE_REMOVABLE)
		mode = workDirInfo.Mode;
	}
	/*
	CParsedPath parsedPath;
	parsedPath.ParsePath(archiveName);
	UINT driveType = GetDriveType(parsedPath.Prefix);
	if ((driveType != DRIVE_CDROM) && (driveType != DRIVE_REMOVABLE))
	  mode = ZipSettings::WorkDir::Mode::kCurrent;
	*/
  }
  switch(mode)
  {
	case WorkDir::Mode::kCurrent:
	{
	  return Common::FileSystem::ExtractDirPrefixFromPath(path);
	}
	case WorkDir::Mode::kSpecified:
	{
	  CString tempDir = workDirInfo.Path;
	  Common::FileSystem::FileName::NormalizeDirPathPrefix(tempDir);
	  return tempDir;
	}
	default:
	{
	  CString tempDir;
	  if (!Common::FileSystem::GetTempPath(tempDir))
		throw 141717;
	  return tempDir;
	}
  }
}

}

}

}